package com.jh.fcsm.util.poi;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.jh.fcsm.common.exception.ServiceException;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.Map.Entry;

/**
 * 阿里excel导入导出工具类
 * dynamicExportExcel 动态导出Excel
 * exportTemplateFill 按模板填充Excel
 * exportExcelTemplate 按模板导出Excel
 * exportExcel 普通导出Excel
 * <p>
 * readExcel 普通读取Excel
 * 其它读取参考 https://alibaba-easyexcel.github.io/quickstart/read.html
 *
 * @author szx
 */
public class EasyExcelUtils {

    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(EasyExcelUtils.class);

    /**
     * excel读取 参考
     * https://alibaba-easyexcel.github.io/quickstart/read.html#%E7%A4%BA%E4%BE%8B%E4%BB%A3%E7%A0%81
     *
     *
     */
    /**
     * 读取 excel
     *
     * @param pojoClass 包装类
     * @param filePath  读取文件地址
     * @return 数据对象
     */
    public static List<?> readExcel(Class<?> pojoClass, String filePath) {
        return EasyExcel.read(filePath).head(pojoClass).sheet().doReadSync();
    }

    /**
     * 读取 excel
     *
     * @param pojoClass 包装类
     * @param in        读取文件地址
     * @return 数据对象
     */
    public static List<?> readExcel(Class<?> pojoClass, InputStream in) {
        return EasyExcel.read(in).head(pojoClass).sheet().doReadSync();
    }

    /**
     * 读取 excel
     *
     * @param pojoClass 包装类
     * @param filePath  读取文件流
     * @param headRow   标题头从第几行开始
     * @param row       从标题后多少行开始读取内容
     * @return 数据对象
     */
    public static List<?> readExcel(Class<?> pojoClass, String filePath, int headRow, int row) {
        DefaultDataListener defaultData = new DefaultDataListener(row);
        EasyExcel.read(filePath, pojoClass, defaultData).headRowNumber(headRow).sheet().doReadSync();
        return defaultData.getList();
    }

    /**
     * 读取 excel
     *
     * @param pojoClass 包装类
     * @param in        读取文件流
     * @param headRow   标题头从第几行开始
     * @param row       从标题后多少行开始读取内容
     * @return 数据对象
     */
    public static List<?> readExcel(Class<?> pojoClass, InputStream in, int headRow, int row) {
        DefaultDataListener defaultData = new DefaultDataListener(row);
        EasyExcel.read(in, pojoClass, defaultData).headRowNumber(headRow).sheet().doReadSync();
        return defaultData.getList();
    }

    /**
     * 动态导出表头 数据 注：list 为list<List<String>> 或者list<bean>（按bean 里面的性排）
     *
     * @param list      数据列表
     * @param head      动态的表头 list<list<String>>
     * @param sheetName sheet页名
     * @param filePath  导出文件地址
     */
    public static void dynamicExportExcel(List<List<?>> list, List<List<?>> head, String sheetName, String filePath) {

        List<List<String>> headList = new ArrayList<List<String>>();
        for (List<?> heads : head) {
            List<String> headde = new ArrayList<String>();
            for (Object he : heads) {
                headde.add(he == null ? null : he.toString());
            }
            headList.add(headde);
        }
        EasyExcel.write(filePath)
                // 这里放入动态头
                .head(headList).sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName)
                // 当然这里数据也可以用 List<List<String>> 去传入
                .doWrite(list);
    }

    /**
     * 动态导出表头 数据 注：list 为list<List<String>> 或者list<bean>（按bean 里面的性排）
     *
     * @param list      数据列表
     * @param head      动态的表头 list<list<String>>
     * @param sheetName sheet页名
     * @param out       导出文件流
     */
    public static void dynamicExportExcel(List<List<?>> list, List<List<?>> head, String sheetName, OutputStream out) {

        List<List<String>> headList = new ArrayList<List<String>>();
        for (List<?> heads : head) {
            List<String> headde = new ArrayList<String>();
            for (Object he : heads) {
                headde.add(he == null ? null : he.toString());
            }
            headList.add(headde);
        }
        EasyExcel.write(out)
                // 这里放入动态头
                .head(headList).sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName)
                // 当然这里数据也可以用 List<List<String>> 去传入
                .doWrite(list);
        try {
            out.close();
        } catch (IOException e) {
            logger.error("输出流关闭失败!", e);
        }
    }

    /**
     * 动态导出表头 数据 注：list 为list<List<String>> 或者list<bean>（按bean 里面的性排）
     *
     * @param list      数据列表
     * @param head      动态的表头 list<list<String>>
     * @param sheetName sheet页名
     * @param response  导出文件流到response下载
     * @param fileName  下载文件名
     */
    public static void dynamicExportExcel(List<List<?>> list, List<List<?>> head, String sheetName,
                                          HttpServletResponse response, String fileName) {

        List<List<String>> headList = new ArrayList<List<String>>();
        for (List<?> heads : head) {
            List<String> headde = new ArrayList<String>();
            for (Object he : heads) {
                headde.add(he == null ? null : he.toString());
            }
            headList.add(headde);
        }
        OutputStream out = null;
        try {
            if (fileName.indexOf(".") < 0) {
                throw new ServiceException("文件名必须带后缀");
            }
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/form-data");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes(), "ISO8859-1"));
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");
            out = response.getOutputStream();
            EasyExcel.write(out)
                    // 这里放入动态头
                    .head(headList).sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName)
                    // 当然这里数据也可以用 List<List<String>> 去传入
                    .doWrite(list);
        } catch (IOException e) {
            throw new ServiceException(e);
        } finally {
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    logger.error("输出流关闭失败!", e);
                }
            }
        }

    }

    /**
     * 动态导出表头 数据 注：dataList 为list<对象>
     *
     * @param dataList  数据对象
     * @param headMap   表头map key为属性名，value为表格动态头，linkedHashMap 保证顺序
     * @param sheetName sheet页名
     * @param response  导出文件流到response下载
     * @param fileName  下载文件名
     */
    public static void dynamicExportExcel(List<Object> dataList, LinkedHashMap<String, String> headMap,
                                          String sheetName, HttpServletResponse response, String fileName) {
        Map<String, List<List<?>>> dataHeadList = dataToList(dataList, headMap);
        dynamicExportExcel(dataHeadList.get("data"), dataHeadList.get("head"), sheetName, response, fileName);
    }

    /**
     * 动态导出表头 数据 注：dataList 为list<对象>
     *
     * @param dataList  数据对象
     * @param headMap   表头map key为属性名，value为表格动态头，linkedHashMap 保证顺序
     * @param sheetName sheet页名
     * @param out       导出文件流
     */
    public static void dynamicExportExcel(List<Object> dataList, LinkedHashMap<String, String> headMap,
                                          String sheetName, OutputStream out) {
        Map<String, List<List<?>>> dataHeadList = dataToList(dataList, headMap);
        dynamicExportExcel(dataHeadList.get("data"), dataHeadList.get("head"), sheetName, out);
    }

    /**
     * 动态导出表头 数据 注：dataList 为list<对象>
     *
     * @param dataList  数据对象
     * @param headMap   表头map key为属性名，value为表格动态头，linkedHashMap 保证顺序
     * @param sheetName sheet页名
     * @param filePath  导出文件地址
     */
    public static void dynamicExportExcel(List<Object> dataList, LinkedHashMap<String, String> headMap,
                                          String sheetName, String filePath) {
        Map<String, List<List<?>>> dataHeadList = dataToList(dataList, headMap);
        dynamicExportExcel(dataHeadList.get("data"), dataHeadList.get("head"), sheetName, filePath);
    }

    /**
     * 组装简单动态excel表头数据
     *
     * @param objList 数据对象
     * @param headMap 简单单行动态头<key(对象属性名) value(中文excel头)> LinkedHashMap 有序的Map
     * @return
     */
    public static Map<String, List<List<?>>> dataToList(List<Object> objList, LinkedHashMap<String, String> headMap) {
        Map<String, List<List<?>>> result = new HashMap<String, List<List<?>>>();
        List<List<?>> headList = new ArrayList<List<?>>();
        List<List<?>> dataList = new ArrayList<List<?>>();
        for (Entry<String, String> entry : headMap.entrySet()) {
            List<Object> data = new ArrayList<Object>();
            for (Object obj : objList) {
                try {
                    String v = BeanUtils.getProperty(obj, entry.getKey());
                    data.add(v);
                } catch (Exception e) {
                    logger.error("数据转换异常", e);
                    throw new ServiceException("数据转换异常");
                }
            }
            dataList.add(data);
            List<String> head = new ArrayList<String>();
            head.add(entry.getValue());
            headList.add(head);
        }
        result.put("head", headList);
        result.put("data", dataList);
        return result;
    }

    /**
     * 填充excle excel模板内{name} 包含的表示变量从data中取属性进行填充 dataList数据列 {.name}表示多行填充 data
     * 与dataList 可同时，也可单个
     *
     * @param data         bean 或map 类型数据
     * @param dataList     数据列表
     * @param sheetName    sheet名字 默认sheet0
     * @param filePath     导出excel路径
     * @param templatePath 模板路径
     * @param direction    填充方向，true向下，false横向 默认向下
     */
    public static void exportTemplateFill(Object data, List<Object> dataList, String sheetName, String filePath,
                                          String templatePath, Boolean direction) {

        ExcelWriter excelWriter = EasyExcel.write(filePath).withTemplate(templatePath).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认
        // 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true
        // 但是这个就会把所有数据放到内存 会很耗内存
        // 如果数据量大 list不是最后一行 参照下一个
        FillConfig fillConfig = null;
        if (direction == null || direction) {
            fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        } else {
            fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
        }
        if (!CollectionUtils.isEmpty(dataList)) {
            excelWriter.fill(dataList, fillConfig, writeSheet);
        }
        if (data != null) {
            excelWriter.fill(data, writeSheet);
        }
        excelWriter.finish();
    }

    /**
     * 填充excle excel模板内{name} 包含的表示变量从data中取属性进行填充 dataList数据列 {.name}表示多行填充 data
     * 与dataList 可同时，也可单个
     *
     * @param data         bean 或map 类型数据
     * @param dataList     数据列表
     * @param sheetName    sheet名字 默认sheet0
     * @param out          导出excel 流
     * @param templatePath 模板路径
     * @param direction    填充方向，true向下，false横向 默认向下
     */
    public static void exportTemplateFill(Object data, List<Object> dataList, String sheetName, OutputStream out,
                                          String templatePath, Boolean direction) {

        ExcelWriter excelWriter = EasyExcel.write(out).withTemplate(templatePath).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认
        // 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true
        // 但是这个就会把所有数据放到内存 会很耗内存
        // 如果数据量大 list不是最后一行 参照下一个
        FillConfig fillConfig = null;
        if (direction == null || direction) {
            fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        } else {
            fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
        }
        if (!CollectionUtils.isEmpty(dataList)) {
            excelWriter.fill(dataList, fillConfig, writeSheet);
        }
        if (data != null) {
            excelWriter.fill(data, writeSheet);
        }
        excelWriter.finish();
        try {
            out.close();
        } catch (IOException e) {
            logger.error("输出流关闭失败!", e);
        }
    }

    /**
     * 填充excle excel模板内{name} 包含的表示变量从data中取属性进行填充 dataList数据列 {.name}表示多行填充 data
     * 与dataList 可同时，也可单个
     *
     * @param data           bean 或map 类型数据
     * @param dataList       数据列表
     * @param sheetName      sheet名字 默认sheet0
     * @param out            导出excel 流
     * @param templateStream 模板流
     * @param direction      填充方向，true向下，false横向 默认向下
     */
    public static void exportTemplateFill(Object data, List<Object> dataList, String sheetName, OutputStream out,
                                          InputStream templateStream, Boolean direction) {

        ExcelWriter excelWriter = EasyExcel.write(out).withTemplate(templateStream).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认
        // 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true
        // 但是这个就会把所有数据放到内存 会很耗内存
        // 如果数据量大 list不是最后一行 参照下一个
        FillConfig fillConfig = null;
        if (direction == null || direction) {
            fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        } else {
            fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
        }
        if (!CollectionUtils.isEmpty(dataList)) {
            excelWriter.fill(dataList, fillConfig, writeSheet);
        }
        if (data != null) {
            excelWriter.fill(data, writeSheet);
        }
        excelWriter.finish();
        try {
            out.close();
        } catch (IOException e) {
            logger.error("输出流关闭失败!", e);
        }
    }

    /**
     * 填充excle excel模板内{name} 包含的表示变量从data中取属性进行填充 dataList数据列 {.name}表示多行填充 data
     * 与dataList 可同时，也可单个
     *
     * @param data         bean 或map 类型数据
     * @param dataList     数据列表
     * @param sheetName    sheet名字 默认sheet0
     * @param fileName     下载文件名字
     * @param response     导出excel 下载流response
     * @param templatePath 模板路径
     * @param direction    填充方向，true向下，false横向 默认向下
     */
    public static void exportTemplateFill(Object data, List<Object> dataList, String sheetName, String fileName,
                                          HttpServletResponse response, String templatePath, Boolean direction) {
        OutputStream out = null;
        try {
            if (fileName.indexOf(".") < 0) {
                throw new ServiceException("文件名必须带后缀");
            }
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/form-data");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes(), "ISO8859-1"));
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");
            out = response.getOutputStream();
            ExcelWriter excelWriter = EasyExcel.write(out).withTemplate(templatePath).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName)
                    .build();

            // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认
            // 是false，会直接使用下一行，如果没有则创建。
            // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
            // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true
            // 但是这个就会把所有数据放到内存 会很耗内存
            // 如果数据量大 list不是最后一行 参照下一个
            FillConfig fillConfig = null;
            if (direction == null || direction) {
                fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            } else {
                fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
            }
            if (!CollectionUtils.isEmpty(dataList)) {
                excelWriter.fill(dataList, fillConfig, writeSheet);
            }
            if (data != null) {
                excelWriter.fill(data, writeSheet);
            }
            excelWriter.finish();
        } catch (IOException e) {
            throw new ServiceException(e);
        } finally {
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    logger.error("输出流关闭失败!", e);
                }
            }
        }
    }

    /**
     * 填充excle excel模板内{name} 包含的表示变量从data中取属性进行填充 dataList数据列 {.name}表示多行填充 data
     * 与dataList 可同时，也可单个
     *
     * @param data           bean 或map 类型数据
     * @param dataList       数据列表
     * @param sheetName      sheet名字 默认sheet0
     * @param fileName       下载文件名字
     * @param response       导出excel 下载流response
     * @param templateStream 模板流
     * @param direction      填充方向，true向下，false横向 默认向下
     */
    public static void exportTemplateFill(Object data, List<Object> dataList, String sheetName, String fileName,
                                          HttpServletResponse response, InputStream templateStream, Boolean direction) {
        OutputStream out = null;
        try {
            if (fileName.indexOf(".") < 0) {
                throw new ServiceException("文件名必须带后缀");
            }
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/form-data");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes(), "ISO8859-1"));
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");
            out = response.getOutputStream();
            ExcelWriter excelWriter = EasyExcel.write(out).withTemplate(templateStream).build();
            WriteSheet writeSheet = EasyExcel.writerSheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName)
                    .build();

            // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认
            // 是false，会直接使用下一行，如果没有则创建。
            // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
            // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true
            // 但是这个就会把所有数据放到内存 会很耗内存
            // 如果数据量大 list不是最后一行 参照下一个
            FillConfig fillConfig = null;
            if (direction == null || direction) {
                fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
            } else {
                fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
            }
            if (!CollectionUtils.isEmpty(dataList)) {
                excelWriter.fill(dataList, fillConfig, writeSheet);
            }
            if (data != null) {
                excelWriter.fill(data, writeSheet);
            }
            excelWriter.finish();
        } catch (IOException e) {
            throw new ServiceException(e);
        } finally {
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    logger.error("输出流关闭失败!", e);
                }
            }
        }
    }

    /**
     * 填充excle excel模板内{name} 包含的表示变量从data中取属性进行填充 dataList数据列 {.name}表示多行填充 data
     * 与dataList 可同时，也可单个
     *
     * @param data           bean 或map 类型数据
     * @param dataList       数据列表
     * @param sheetName      sheet名字 默认sheet0
     * @param filePath       导出excel路径
     * @param templateStream 模板文件流
     * @param direction      填充方向，true向下，false横向 默认向下
     */
    public static void exportTemplateFill(Object data, List<Object> dataList, String sheetName, String filePath,
                                          InputStream templateStream, Boolean direction) {

        ExcelWriter excelWriter = EasyExcel.write(filePath).withTemplate(templateStream).build();
        WriteSheet writeSheet = EasyExcel.writerSheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).build();

        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行，然后下面的数据往后移动。默认
        // 是false，会直接使用下一行，如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了，所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行，下面还有数据需要填充 就必须设置 forceNewRow=true
        // 但是这个就会把所有数据放到内存 会很耗内存
        // 如果数据量大 list不是最后一行 参照下一个
        FillConfig fillConfig = null;
        if (direction == null || direction) {
            fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        } else {
            fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
        }
        if (!CollectionUtils.isEmpty(dataList)) {
            excelWriter.fill(dataList, fillConfig, writeSheet);
        }
        if (data != null) {
            excelWriter.fill(data, writeSheet);
        }
        excelWriter.finish();
    }

    /**
     * 按模板导出excel
     *
     * @param list         数据LIST
     * @param sheetName    sheet名字 默认sheet0
     * @param pojoClass    bean 配置class
     * @param filePath     导出excel路径
     * @param templatePath 模板路径
     */
    public static void exportExcelTemplate(List<?> list, String sheetName, Class<?> pojoClass, String filePath,
                                           String templatePath) {
        EasyExcel.write(filePath, pojoClass).withTemplate(templatePath).needHead(false)
                .sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
    }

    /**
     * 按模板导出excel
     *
     * @param list           数据LIST
     * @param sheetName      sheet名字 默认sheet0
     * @param pojoClass      bean 配置class
     * @param filePath       导出excel路径
     * @param templateStream 模板文件流
     */
    public static void exportExcelTemplate(List<?> list, String sheetName, Class<?> pojoClass, String filePath,
                                           InputStream templateStream) {
        EasyExcel.write(filePath, pojoClass).withTemplate(templateStream).needHead(false)
                .sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
    }

    /**
     * 按模板导出excel
     *
     * @param list           数据LIST
     * @param sheetName      sheet名字 默认sheet0
     * @param pojoClass      bean 配置class
     * @param out            导出excel流
     * @param templateStream 模板文件流
     */
    public static void exportExcelTemplate(List<?> list, String sheetName, Class<?> pojoClass, OutputStream out,
                                           InputStream templateStream) {
        EasyExcel.write(out, pojoClass).withTemplate(templateStream).needHead(false)
                .sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
        try {
            out.close();
        } catch (IOException e) {
            logger.error("输出流关闭失败!", e);
        }
    }

    /**
     * 按模板导出excel
     *
     * @param list         数据LIST
     * @param sheetName    sheet名字 默认sheet0
     * @param pojoClass    bean 配置class
     * @param out          导出excel流
     * @param templatePath 模板路径
     */
    public static void exportExcelTemplate(List<?> list, String sheetName, Class<?> pojoClass, OutputStream out,
                                           String templatePath) {
        EasyExcel.write(out, pojoClass).withTemplate(templatePath).needHead(false)
                .sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
        try {
            out.close();
        } catch (IOException e) {
            logger.error("输出流关闭失败!", e);
        }
    }

    /**
     * 按bean 配置下载excel 到页面
     *
     * @param list         数据List
     * @param sheetName    sheet名字 默认sheet0
     * @param pojoClass    bean 配置class
     * @param fileName     导出文件名
     * @param response
     * @param templatePath 模板路径
     */
    public static void exportExcelTemplate(List<?> list, String sheetName, Class<?> pojoClass, String fileName,
                                           HttpServletResponse response, String templatePath) {
        OutputStream out = null;
        try {
            if (fileName.indexOf(".") < 0) {
                throw new ServiceException("文件名必须带后缀");
            }
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/form-data");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes(), "ISO8859-1"));
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");
            out = response.getOutputStream();
            EasyExcel.write(out, pojoClass).withTemplate(templatePath).needHead(false)
                    .sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
        } catch (IOException e) {
            throw new ServiceException(e);
        } finally {
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    logger.error("输出流关闭失败!", e);
                }
            }
        }
    }

    /**
     * 按bean 配置下载excel 到页面
     *
     * @param list           数据List
     * @param sheetName      sheet名字 默认sheet0
     * @param pojoClass      bean 配置class
     * @param fileName       导出文件名
     * @param response
     * @param templateStream 模板文件流
     */
    public static void exportExcelTemplate(List<?> list, String sheetName, Class<?> pojoClass, String fileName,
                                           HttpServletResponse response, InputStream templateStream) {
        OutputStream out = null;
        try {
            if (fileName.indexOf(".") < 0) {
                throw new ServiceException("文件名必须带后缀");
            }
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/form-data");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + new String(fileName.getBytes(), "ISO8859-1"));
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");
            out = response.getOutputStream();
            EasyExcel.write(out, pojoClass).withTemplate(templateStream).needHead(false)
                    .sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
        } catch (IOException e) {
            throw new ServiceException(e);
        } finally {
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    logger.error("输出流关闭失败!", e);
                }
            }
        }
    }

    /**
     * 按bean 配置 导出excel 到path路径
     *
     * @param list      数据LIST
     * @param sheetName sheet名字 默认sheet0
     * @param pojoClass bean 配置class
     * @param filePath  导出excel路径
     */
    public static void exportExcel(List<?> list, String sheetName, Class<?> pojoClass, String filePath) {
        EasyExcel.write(filePath, pojoClass).sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
    }

    /**
     * 按bean 配置 导出excel 到输出流 并关闭流
     *
     * @param list      数据LIST
     * @param sheetName sheet名字 默认sheet0
     * @param pojoClass bean 配置class
     * @param out       输出流
     */
    public static void exportExcel(List<?> list, String sheetName, Class<?> pojoClass, OutputStream out) {
        EasyExcel.write(out, pojoClass).sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
        try {
            out.close();
        } catch (IOException e) {
            logger.error("输出流关闭失败!", e);
        }
    }

    /**
     * 按bean 配置下载excel 到页面
     *
     * @param list      数据List
     * @param sheetName sheet名字 默认sheet0
     * @param pojoClass bean 配置class
     * @param fileName  导出文件名
     * @param response
     */
    public static void exportExcel(List<?> list, String sheetName, Class<?> pojoClass, String fileName,
                                   HttpServletResponse response) {
        OutputStream out = null;
        try {
            if (fileName.indexOf(".") < 0) {
                throw new ServiceException("文件名必须带后缀");
            }
            response.reset();
            //response.setCharacterEncoding("UTF-8");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("connection", "close");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");
            out = response.getOutputStream();
            EasyExcel.write(out, pojoClass).sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName).doWrite(list);
        } catch (IOException e) {
            throw new ServiceException(e);
        } finally {
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    logger.error("输出流关闭失败!", e);
                }
            }
        }
    }

    /**
     * @param list
     * @param sheetName
     * @param pojoClass
     * @param fileName
     * @param response  根据模板导出数据
     * @author szx
     * @date 2021/12/14
     */
    public static void exportExcelByModel(List<?> list, String sheetName, Class<?> pojoClass, String fileName,
                                          HttpServletResponse response, String templateFile) {
        OutputStream out = null;
        try {
            if (fileName.indexOf(".") < 0) {
                throw new ServiceException("文件名必须带后缀");
            }
            response.reset();
            //response.setCharacterEncoding("UTF-8");
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setHeader("connection", "close");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8.name()));
            response.setHeader("Set-Cookie", "fileDownload=true; path=/");
            out = response.getOutputStream();
//            EasyExcel.write(out, pojoClass)
//                    .withTemplate("src/main/resources/static/template/cityAppraisal.xlsx")
//                    .sheet(StringUtils.isEmpty(sheetName) ? "sheet0" : sheetName)
//                    .doWrite(list);

            EasyExcel.write(out, pojoClass).withTemplate(templateFile).sheet(sheetName).doFill(list);
        } catch (IOException e) {
            throw new ServiceException(e);
        } finally {
            if (out != null) {
                try {
                    out.flush();
                    out.close();
                } catch (IOException e) {
                    logger.error("输出流关闭失败!", e);
                }
            }
        }
    }

}

class DefaultDataListener extends AnalysisEventListener<Object> {
    int row;
    int count = 0;
    private List<Object> list = new ArrayList<Object>();

    public DefaultDataListener(int row) {
        this.row = row;
    }

    public List<Object> getList() {
        return list;
    }

    public void setList(List<Object> list) {
        this.list = list;
    }

    public void invoke(Object data, AnalysisContext context) {
        count++;
        if (count > row) {
            list.add(data);
        }
    }

    public void doAfterAllAnalysed(AnalysisContext context) {
        // 所有数据解析完成
    }

}
